﻿// <copyright file="TransformUtilities.cs" company="Microsoft Corporation">
// Copyright (c) 2008 All Right Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-01-29</date>
// <summary>Transformation utilities used as an extension object during XSL Transformation</summary>

namespace Synergist.WordToXamlLibrary
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Transformation utilities used as an extension object during XSL Transformation
    /// </summary>
    public class TransformUtilities
    {
        /// <summary>
        /// Substitute all { for {{ and all } for }} so XAML text parses correctly.
        /// </summary>
        /// <param name="input">a possibly dirty XAML string.</param>
        /// <returns>a clean XAML string</returns>
        /// <remarks>This will work for all text except for text with the brackets reversed like this: } { </remarks>
        public string CleanXaml(string input)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            string value = input.Replace("{", "{}{");

            return value;
        }

        /// <summary>
        /// Given an offset determine the length in 96 DPI Pixels
        /// </summary>
        /// <param name="offset">an offset string</param>
        /// <returns>the length in 96 DPI pixels to the offset</returns>
        public double GetLength(string offset)
        {
            var pt = GetPoint(offset);
            
            return Math.Sqrt(Math.Pow(pt[0], 2) + Math.Pow(pt[1], 2));
        }

        /// <summary>
        /// Given an offset, determine the angle from 0 to 360 with 0 being the positive X Axis
        /// </summary>
        /// <param name="offset">an offset like '3pt,2pt' or ',4pt'</param>
        /// <returns>the angle in degrees</returns>
        public double GetAngle(string offset)
        {
            var pt = GetPoint(offset);

            var radians = Math.Atan2(-pt[1], pt[0]);

            var degrees = radians * 180 / Math.PI;

            if (degrees < 0.0)
            {
                degrees += 360;
            }

            return degrees;
        }

        /// <summary>
        /// Returns the corner radius string for a Border
        /// </summary>
        /// <param name="arcSize">the arc size string as a percentage or a fixed precision number (f suffix)</param>
        /// <param name="widthValue">the width of the element</param>
        /// <param name="heightValue">the height of the element</param>
        /// <returns>the corner radius in 96 DPI pixels</returns>
        public string CornerRadius(string arcSize, string widthValue, string heightValue)
        {
            if (string.IsNullOrEmpty(widthValue))
            {
                throw new ArgumentNullException("widthValue");
            }

            if (string.IsNullOrEmpty(heightValue))
            {
                throw new ArgumentNullException("heightValue");
            }

            if (string.IsNullOrEmpty(arcSize))
            {
                throw new ArgumentNullException("arcSize");
            }

            double width = 0;

            double height = 0;

            if (widthValue.Contains("pt"))
            {
                width = double.Parse(widthValue.Substring(0, widthValue.Length - 2), System.Globalization.CultureInfo.InvariantCulture) * 96 / 72;
            }
            else
            {
                width = double.Parse(widthValue, System.Globalization.CultureInfo.InvariantCulture) * 96 / 72 / 20;
            }

            if (heightValue.Contains("pt"))
            {
                height = double.Parse(heightValue.Substring(0, heightValue.Length - 2), System.Globalization.CultureInfo.InvariantCulture) * 96 / 72;
            }
            else
            {
                height = double.Parse(heightValue, System.Globalization.CultureInfo.InvariantCulture) * 96 / 72 / 20;
            }

            double percentage = 0;

            if (arcSize.Contains('f'))
            {
                percentage = double.Parse(arcSize.Substring(0, arcSize.Length - 1), System.Globalization.CultureInfo.InvariantCulture) / 65536;
            }
            else if (arcSize.Contains('%'))
            {
                percentage = double.Parse(arcSize.Substring(0, arcSize.Length - 1), System.Globalization.CultureInfo.InvariantCulture);
            }
            else
            {
                percentage = double.Parse(arcSize, System.Globalization.CultureInfo.InvariantCulture);
            }

            var size = Math.Min(width, height) * percentage;

            return size.ToString(CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Convert a VML v:shapetype/@path string to a WPF Path data string
        /// </summary>
        /// <param name="vmlPath">a VML path like <![CDATA[m@4@5l@4@11@9@11@9@5xe]]></param>
        /// <returns>a WPF path like this: m 4,5 l 4,11 9,11 9,5 z</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Necessary to be used in XSL Transformation."), 
         System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vml", Justification = "vml is an acronym.")]
        public string GetPathData(string vmlPath)
        {
            if (vmlPath == null)
            {
                return null;
            }

            System.Diagnostics.Debug.WriteLine("Converting VML Path from " + vmlPath);

            var commandKeys = new string[] { "nf", "ns", "ae", "al", "at", "ar", "wa", "wr", "qx", "qy", "qb", "m", "l", "c", "x", "e", "t", "r", "v" };

            string path = vmlPath.Replace('@', ' ');

            foreach (var commandKey in commandKeys)
            {
                path = path.Replace(commandKey, " " + commandKey);
            }

            string[] elements = path.Split(' ');

            StringBuilder builder = new StringBuilder();

            for (int i = 0; i < elements.Length; i++)
            {
                switch (elements[i])
                {
                    case "m":
                        builder.AppendFormat("m {0},{1} ", elements[i + 1], elements[i + 2]);
                        i += 2;
                        break;

                    case "l":
                        builder.Append("l ");
                        i++;
                        do
                        {
                            builder.AppendFormat("{0},{1} ", elements[i], elements[i + 1]);

                            i += 2;
                        }
                        while (!commandKeys.Contains(elements[i]));
                        i--;

                        break;

                    case "x":
                        builder.Append("z");
                        break;

                    case "e":
                        break;
                }
            }

            string wpfPathData = builder.ToString();

            System.Diagnostics.Debug.WriteLine("to WPF Path Data: " + wpfPathData);

            return wpfPathData;
        }

        /// <summary>
        /// Gets the point for an offset
        /// </summary>
        /// <param name="offset">the offset string</param>
        /// <returns>a double[2] with x and y coordinates</returns>
        private static double[] GetPoint(string offset)
        {
            var xy = offset.Split(',');

            double[] point = new double[2];

            if (xy[0].Contains("pt"))
            {
                var xstr = xy[0].Substring(0, xy[0].Length - 2);

                point[0] = double.Parse(xstr, System.Globalization.CultureInfo.InvariantCulture) * 96.0 / 72.0;
            }
            else if (!string.IsNullOrEmpty(xy[0]))
            {
                point[0] = double.Parse(xy[0], System.Globalization.CultureInfo.InvariantCulture) * 96.0 / 72.0 / 20;
            }

            if (xy.Length == 1)
            {
                point[1] = point[0];
            }
            else if (xy[1].Contains("pt"))
            {
                var ystr = xy[1].Substring(0, xy[1].Length - 2);

                point[1] = double.Parse(ystr, System.Globalization.CultureInfo.InvariantCulture) * 96.0 / 72.0;
            }
            else if (!string.IsNullOrEmpty(xy[1]))
            {
                point[1] = double.Parse(xy[1], System.Globalization.CultureInfo.InvariantCulture) * 96.0 / 72.0 / 20;
            }

            return point;
        }
    }
}
